//============================================================================
// Copyright (C) 2013 Brett R. Jones
//
// Issued to public domain 2013
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include <string.h>
#include <memory.h>
#include <stdio.h>
#include "VxInetAddress.h"
#include "VxSktUtil.h"
#include "vx_debug.h"
#ifdef WIN_PLATFORM
	#include "Winsock2.h"
	#include <Ws2tcpip.h>  
#else
    #include <netdb.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <net/if.h>
	#ifndef ANDROID_PLATFORM
		#include <ifaddrs.h>
	#endif
#endif

#define IP4_PREFIX 0x0000ffff00000000ULL
#define INET6_MAX_STR_LEN 68

//============================================================================
VxInetAddress::VxInetAddress()
: m_u64AddrHi(0)
, m_u64AddrLo(0)
{
}

//============================================================================
VxInetAddress::VxInetAddress( const char * pIpAddress )
: m_u64AddrHi(0)
, m_u64AddrLo(0)
{
	fromString( pIpAddress );
}

//============================================================================
VxInetAddress::VxInetAddress( U32 u32IpAddr )
{
	setIp( u32IpAddr );
}

/*
//============================================================================
const char * DecodeError(int iErrorCode)
{
	static char Message[1024];
#ifdef WIN_PLATFORM
	// If this program was multi-threaded, we'd want to use FORMAT_MESSAGE_ALLOCATE_BUFFER
	// instead of a static buffer here.
	// (And of course, free the buffer when we were done with it)
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
		FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, iErrorCode,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)Message, 1024, NULL);
#else
	sprintf( Message, "VxInetAddress error %d\n", iErrorCode );
#endif // WIN_PLATFORM

	return Message;
}
*/

//============================================================================
VxInetAddress VxInetAddress::getDefaultIp( void )
{
	 std::vector<VxInetAddress> retAddresses;
	 getAllAddresses( retAddresses );
	 if( retAddresses.size() )
	 {
		 return retAddresses[0];
	 }
	 return VxInetAddress();
}

//============================================================================
int VxInetAddress::getAllAddresses( std::vector<VxInetAddress>& retAddresses )
{
#if defined(WIN_PLATFORM) || defined(ANDROID_PLATFORM)
	char as8HostName[ 1025 ];
	//first get host name
	if( gethostname( as8HostName, sizeof( as8HostName ) ) )
	{
        log_msg( LOG_ERROR, "getAllAddresses: Unable to get host name\n" );
		#ifdef WIN_PLATFORM
			return WSAGetLastError();
		#else
			return VxGetLastError();
		#endif // WIN_PLATFORM
	}

	struct addrinfo Hints;
	struct addrinfo * AI;
	struct addrinfo * AddrInfo;

	memset(&Hints, 0, sizeof(Hints));

    Hints.ai_family   = PF_UNSPEC;
    Hints.ai_socktype = SOCK_STREAM;
    Hints.ai_protocol = IPPROTO_TCP;
    Hints.ai_flags    = AI_PASSIVE;


	char as8Buf[16];
	sprintf( as8Buf, "%d", 65000 );

	int RetVal = getaddrinfo(as8HostName, "echo", &Hints, &AddrInfo);
	if (RetVal != 0)
	{
		//char * pErr = gai_strerror(RetVal);
		//printf("getaddrinfo() failed with error %d: %s\n", RetVal, pErr );
        log_msg( LOG_ERROR, "VxInetAddress::getAllAddresses getaddrinfo error %d\n", RetVal );
		return RetVal;
	}

	for(  AI = AddrInfo; AI != NULL; AI = AI->ai_next )
	{
		if ((AI->ai_family != PF_INET) && (AI->ai_family != PF_INET6))
		{
			continue;
		}
		// Open a socket with the correct address family for this address.
		SOCKET oSkt = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);

		if( oSkt == INVALID_SOCKET )
		{
			continue;
		}

        struct sockaddr_storage * poSktAddr = (struct sockaddr_storage *)AI->ai_addr;
        VxInetAddress oTestAddr;
        VxSetSktAddressPort(poSktAddr, 0);
        oTestAddr.setIp( *poSktAddr );
        std::string strTestIpAddress;
        oTestAddr.toStdString( strTestIpAddress );

        if( ( false == oTestAddr.isValid() ) ||
            ( oTestAddr.isLoopBack() ) )
        {
            if( oTestAddr.isLoopBack() )
            {
                log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: loopback addr %s\n", strTestIpAddress.c_str() );
            }
            else
            {
                log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: invalid addr %s\n", strTestIpAddress.c_str() );
            }
			log_msg( LOG_DEBUG, "closing loopback skt %d\n", oSkt );
            VxCloseSkt( oSkt );
            continue;
        }

		log_msg( LOG_DEBUG, "binding skt %d\n", oSkt );
		if( false == VxBindSkt( oSkt, poSktAddr ) )
        {
            log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: could not bind addr %s\n", strTestIpAddress.c_str() );
            continue;
        }

        if( oTestAddr.isValid() &&
            ( false == oTestAddr.isLoopBack() ) )
        {
            retAddresses.push_back(oTestAddr);
        }

//#define TEST_CONNECTION
#ifdef TEST_CONNECTION
        if( false == VxTestConnection( oSkt, oTestAddr ) )
        {
            log_msg( LOG_DEBUG, "Connection using local address %s OK\n", strTestIpAddress.c_str() );
        }
        else
        {
            log_msg( LOG_DEBUG, "Connection using local address %s FAIL\n", strTestIpAddress.c_str() );
        }
#else
		log_msg( LOG_DEBUG, "closing skt %d\n", oSkt );
        VxCloseSkt( oSkt );
#endif // TEST_CONNECTION

	}
	log_msg( LOG_DEBUG, "freeing addr info\n" );
	freeaddrinfo( AddrInfo ); // free the linked list
#else
    struct ifaddrs *myaddrs, *ifa;
    struct sockaddr_storage * poSktAddr = NULL;
    char buf[64];

    if(getifaddrs(&myaddrs) != 0)
    {
        perror("VxInetAddress::getAllAddresses: getifaddrs");
        log_msg( LOG_ERROR, "VxInetAddress::getAllAddresses: getifaddrs FAIL\n" );
    }

    for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
    {
        if (ifa->ifa_addr == NULL)
            continue;
        if (!(ifa->ifa_flags & IFF_UP))
            continue;

        switch (ifa->ifa_addr->sa_family)
        {
            case AF_INET:
            case AF_INET6:
            {
                poSktAddr = (struct sockaddr_storage *)ifa->ifa_addr;
                break;
            }

            default:
                continue;
        }

        if (!inet_ntop(ifa->ifa_addr->sa_family, poSktAddr, buf, sizeof(buf)))
        {
           log_msg( LOG_ERROR, "VxInetAddress::getAllAddresses: %s: inet_ntop failed!\n", ifa->ifa_name);
        }
        else
        {
            // Open a socket with the correct address family for this address.
            SOCKET oSkt = socket(ifa->ifa_addr->sa_family,
                                 SOCK_STREAM,
                                 0 );

            if( oSkt == INVALID_SOCKET )
            {
                continue;
            }
            log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: %s: %s\n", ifa->ifa_name, buf);

            VxInetAddress oTestAddr;
            VxSetSktAddressPort(poSktAddr, 0);
            oTestAddr.setIp( *poSktAddr );
            std::string strTestIpAddress;
            oTestAddr.toStdString( strTestIpAddress );

            if( ( false == oTestAddr.isValid() ) ||
                ( oTestAddr.isLoopBack() ) )
            {
                if( oTestAddr.isLoopBack() )
                {
                    log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: loopback addr %s\n", strTestIpAddress.c_str() );
                }
                else
                {
                    log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: invalid addr %s\n", strTestIpAddress.c_str() );
                }
                VxCloseSkt( oSkt );
                continue;
            }

            if( false == VxBindSkt( oSkt, poSktAddr ) )
            {
                log_msg( LOG_DEBUG, "VxInetAddress::getAllAddresses: could not bind addr %s\n", strTestIpAddress.c_str() );
                VxCloseSkt( oSkt );
                continue;
            }

            if( oTestAddr.isValid() &&
                ( false == oTestAddr.isLoopBack() ) )
            {
                retAddresses.push_back(oTestAddr);
            }

    //#define TEST_CONNECTION
    #ifdef TEST_CONNECTION
            if( false == VxTestConnection( oSkt, oTestAddr ) )
            {
                log_msg( LOG_DEBUG, "Connection using local address %s OK\n", strTestIpAddress.c_str() );
            }
            else
            {
                log_msg( LOG_DEBUG, "Connection using local address %s FAIL\n", strTestIpAddress.c_str() );
            }
    #else
            VxCloseSkt( oSkt );
    #endif // TEST_CONNECTION
        }
    }

    freeifaddrs(myaddrs);
#endif // WIN_PLATFORM
	log_msg( LOG_DEBUG, "exit VxInetAddress:;getAllAddresses\n" );
	return 0;
}


//============================================================================
bool VxInetAddress::isLoopBack()
{
	bool isLoopBack = true;
	if( isIPv4() )
	{
		if( 0x7f000001 != getIPv4AddressInNetOrder() )
		{
			isLoopBack = false;
		}
	}
	else
	{
		U8 * pu8Bytes = (U8 * )this;
		for(int i = 0; i < 16; i++ )
		{
			if( pu8Bytes[i] && (i != 15) )
			{
				isLoopBack = false;
				break;
			}
			else if(  (1 == pu8Bytes[i]) && (i == 15) )
			{
				isLoopBack = true;
				break;
			}
		}
	}
	return isLoopBack;
}

//============================================================================
bool VxInetAddress::runTests()
{
	/*
	bool testPass = true;
	char * pIPv4 = "1.2.3.4";
	char * pIPv6 = "1:2:3:4:5::6";

	char ipBuf[INET_ADDRSTRLEN];  // space to hold the IPv4 string

	// IPv4 tests
	fromString( pIPv4 );
	std::string strResult = toStdString();
	if( false == ( strResult == pIPv4))
	{
		testPass = false;
	}

	// normal way
	struct sockaddr_in sa4;      
	inet_pton(AF_INET, pIPv4, &(sa4.sin_addr));

	U32 u32Actual = htonl( getIPv4Address() );
	if( *((U32*)&sa4.sin_addr) != u32Actual )
	{
		testPass = false;
	}

	fillAddress(sa4, 0);
	u32Actual = htonl( getIPv4Address() );
	if( *((U32*)&sa4.sin_addr) != u32Actual )
	{
		testPass = false;
	}

	// IPv6 tests
	struct sockaddr_in6 sa6;      
	inet_pton(AF_INET6, pIPv6, &(sa6.sin6_addr));


	inet_ntop(AF_INET6, &(sa6.sin6_addr), ipBuf, INET_ADDRSTRLEN);

	setIp( pIPv6 );
	strResult = toStdString();
	if( false == ( strResult == ipBuf))
	{
		testPass = false;
	}

	setIp( sa6 );
	strResult = toStdString();
	if( false == ( strResult == ipBuf))
	{
		testPass = false;
	}


	return testPass;
		*/
	return false;
}

//============================================================================
//! equal operator
VxInetAddress& VxInetAddress::operator=(const VxInetAddress& oAddr) 
{
	if( this != &oAddr )
	{
		m_u64AddrHi = oAddr.m_u64AddrHi;
		m_u64AddrLo = oAddr.m_u64AddrLo;
	}
	return *this;
}

//============================================================================
bool VxInetAddress::operator != (const VxInetAddress& oAddr)  const
{
	return (m_u64AddrHi != oAddr.m_u64AddrHi) || (m_u64AddrLo != oAddr.m_u64AddrLo);
}

//============================================================================
bool VxInetAddress::operator == (const VxInetAddress& oAddr)  const
{
	return (m_u64AddrHi == oAddr.m_u64AddrHi) && (m_u64AddrLo == oAddr.m_u64AddrLo);
}

//============================================================================
bool VxInetAddress::isValid( void )
{
	return ( 0 != m_u64AddrLo );
}

//============================================================================
void VxInetAddress::setToInvalid( void )
{
	m_u64AddrHi = 0;
	m_u64AddrLo = 0;
}

//============================================================================
void VxInetAddress::fromString( const char * pIpAddress )
{
	if( ( NULL == pIpAddress ) || 
		( 0 == strlen( pIpAddress ) ) ||
		( 0 == strcmp( "0.0.0.0", pIpAddress ) ) ||
		( 0 == strcmp( "::", pIpAddress ) ) )
	{
		setToInvalid();
	}
	else if( isIPv4String( pIpAddress ) )
	{
		U32 u32Ip;
		VxIPv4_pton( pIpAddress, &u32Ip, false );
		setIp( u32Ip );
	}
	else
	{
        VxIPv6_pton( pIpAddress, this );
	}
}

//============================================================================
bool VxInetAddress::isIPv4String( const char * pIpAddress )
{
	if( pIpAddress &&
		(strlen(pIpAddress) < 16 ) &&
		strchr(pIpAddress, '.') &&
		( 0 == strchr(pIpAddress, ':')) )
	{
		return true;
	}
	return false;
}

//============================================================================
bool VxInetAddress::isLittleEndian( void )
{
	U16 port = 0x1234;
	if( port != htons(port))
	{
		return true;
	}
	return false;
}

//============================================================================
bool VxInetAddress::isIPv4( void )
{
	if( (false == isValid() ) ||
		(0xffffffffffffffffULL == m_u64AddrHi)) 
	{
		return true;
	}
	return false;
}

//============================================================================
bool VxInetAddress::isIPv6( void )
{
	return isIPv4() ? false : true;
}

//============================================================================
bool VxInetAddress::isIPv6GlobalAddress( void )
{
	if( isIPv6() && 
		( 0x2001 == htons( *((U16 *)this) ) ) )
	{
		return true;
	}
	return false;
}

//============================================================================
U32 VxInetAddress::getIPv4AddressInHostOrder( void )
{
	return ntohl(*((U32*)&m_u64AddrLo));
}

//============================================================================
U32 VxInetAddress::getIPv4AddressInNetOrder( void )
{
	return *((U32*)&m_u64AddrLo);
}

//============================================================================
// note.. u32IPv4Addr must be in host order
void VxInetAddress::setIp( U32 u32IPv4Addr, bool bIsHostOrder )
{
	if( bIsHostOrder )
	{
		u32IPv4Addr = htonl( u32IPv4Addr );
	}
	if( u32IPv4Addr )
	{
		m_u64AddrLo = 0;
		*((U32*)&m_u64AddrLo) = u32IPv4Addr;
		m_u64AddrHi = 0xffffffffffffffffULL;
	}
	else
	{
		setToInvalid();
	}
}

//============================================================================
void VxInetAddress::setIp( const char * pIp )
{
	fromString( pIp );
}

//============================================================================
//! returns port in host order
U16 VxInetAddress::setIp( struct sockaddr_in& oIPv4Addr )
{
	setIp(*((U32*)&oIPv4Addr.sin_addr), true );
	return ntohs(oIPv4Addr.sin_port);
}

//============================================================================
//! returns port in host order
U16 VxInetAddress::setIp( struct sockaddr_in6& oIPv6Addr )
{
	memcpy(this, &oIPv6Addr.sin6_addr, 16);
	return ntohs(oIPv6Addr.sin6_port);
}

//============================================================================
//! returns port in host order
U16 VxInetAddress::setIp( struct sockaddr_storage& oAddr )
{
	switch( oAddr.ss_family ) 
	{
	case AF_INET:
		return setIp(*((struct sockaddr_in *)&oAddr));
		break;

	case AF_INET6:
		return setIp(*((struct sockaddr_in6 *)&oAddr));
		break;

	default:
		//vx_assert(false);
		return 0;
	}
}

//============================================================================
//! fill address with this ip address and the given port
int VxInetAddress::fillAddress( struct sockaddr_storage& oAddr, U16 u16Port )
{
	if( isIPv4() )
	{
        return fillAddress( *((struct sockaddr_in*)&oAddr), u16Port );
	}
	else
	{
        return fillAddress( *((struct sockaddr_in6*)&oAddr), u16Port );
	}
}

//============================================================================
//! fill address with this ip address and the given port.. returns struct len
int VxInetAddress::fillAddress( struct sockaddr_in& oIPv4Addr, U16 u16Port )
{
	// setup the address and port
	memset( &oIPv4Addr, 0, sizeof( sockaddr_in ) );

	oIPv4Addr.sin_family			= AF_INET;
	*((long*)&oIPv4Addr.sin_addr)	= getIPv4AddressInHostOrder();

	oIPv4Addr.sin_port				= htons( u16Port );
	return (int)sizeof( struct sockaddr_in);
}

//============================================================================
//! fill address with this ip address and the given port.. returns struct len
int VxInetAddress::fillAddress( struct sockaddr_in6& oIPv6Addr, U16 u16Port )
{
	// setup the address and port
	memset( &oIPv6Addr, 0, sizeof( sockaddr_in6 ) );

	oIPv6Addr.sin6_family			= AF_INET6;

	memcpy( &oIPv6Addr.sin6_addr, this, 16 );

	oIPv6Addr.sin6_port	= htons( u16Port );
	return (int)sizeof( struct sockaddr_in6);
}


//============================================================================
void VxInetAddress::toStdString( std::string& retIpAddress )
{
	char as8Buf[ INET6_MAX_STR_LEN ];
	as8Buf[0] = 0; 

	if( isValid() )
	{
		if( isIPv4() )
		{
			U32 u32Ip = getIPv4AddressInNetOrder();
            VxIPv4_ntop( &u32Ip, as8Buf, sizeof( as8Buf ), false );
		}
		else
		{
			VxIPv6_ntop( this, as8Buf, sizeof( as8Buf ) );
		}
		retIpAddress = as8Buf;
	}
	else
	{
		retIpAddress = "0.0.0.0";
	}
}

//============================================================================
std::string VxInetAddress::toStdString( void )
{
	std::string retIpAddress;
	toStdString( retIpAddress );
	return retIpAddress;
}

//============================================================================
//! returns port in host order
U16	VxInetAddress::getIpFromAddr(const struct sockaddr *sa, std::string& retStr)
{
	U16 u16Port = 0;
	char as8Addr[ INET6_MAX_STR_LEN ];
    switch(sa->sa_family)
    {
		case AF_INET:
			VxIPv4_ntop(&(((struct sockaddr_in *)sa)->sin_addr), as8Addr, sizeof(as8Addr));
			u16Port = ntohs( (((struct sockaddr_in *)sa)->sin_port) );
			break;

		case AF_INET6:
			VxIPv6_ntop( &(((struct sockaddr_in6 *)sa)->sin6_addr), as8Addr, sizeof(as8Addr));
			u16Port = ntohs( (((struct sockaddr_in6 *)sa)->sin6_port) );
			break;

		default:
			strcpy(as8Addr, "Unknown AF");
            return 0;
	}
    retStr = as8Addr;
	return u16Port;
}

//============================================================================
bool VxInetAddress::is64BitLittleEndianOs( void )
{
	static U64 testVal = 0x0101;
	if( ((char *)&testVal)[0] )
	{
		return true;
	}
	return false;
}

//============================================================================
U64 VxInetAddress::swap64Bit( U64 src )
{
	U64 dest;
	U16 * pu8Src = (U16 *)&src;
	U16 * pu8Dest = (U16 *)&dest;
	pu8Dest[2] = htons(pu8Src[0]);
	pu8Dest[1] = htons(pu8Src[1]);
	pu8Dest[4] = htons(pu8Src[2]);
	pu8Dest[3] = htons(pu8Src[3]);
	return dest;
}

//============================================================================
void VxInetAddress::litteEndianToNetIPv6( U16 * src, U16 * dest )
{
	if( is64BitLittleEndianOs() )
	{
		U64 u64Hi = *((U64 *)src);
		U64 u64Lo = *((U64 *)(&src[4]));
		*((U64 *)dest) = swap64Bit(u64Hi);
		*((U64 *)(&dest[4])) = swap64Bit(u64Lo);
	}
	else
	{
		U32 u32Hi1 = *((U32 *)src);
		U32 u32Hi2 = *((U32 *)(&src[2]));
		U32 u32Lo1 = *((U32 *)(&src[4]));
		U32 u32Lo2 = *((U32 *)(&src[6]));
		*((U32 *)dest) = htonl(u32Hi2);
		*((U32 *)(&dest[2])) = htonl(u32Hi1);
		*((U32 *)(&dest[4])) = htonl(u32Lo2);
		*((U32 *)(&dest[6])) = htonl(u32Lo1);
	}
}

//============================================================================
VxInetAddrAndPort::VxInetAddrAndPort()
: m_u16Port(0)
{

}

//============================================================================
//! equal operator
VxInetAddrAndPort& VxInetAddrAndPort::operator=(const VxInetAddress& oAddr) 
{
	if( this != &oAddr )
	{
		m_u64AddrHi = oAddr.m_u64AddrHi;
		m_u64AddrLo = oAddr.m_u64AddrLo;
	}
	return *this;
}

//============================================================================
//! equal operator
VxInetAddrAndPort& VxInetAddrAndPort::operator=(const VxInetAddrAndPort& oAddr) 
{
	if( this != &oAddr )
	{
		m_u64AddrHi = oAddr.m_u64AddrHi;
		m_u64AddrLo = oAddr.m_u64AddrLo;
		m_u16Port = oAddr.m_u16Port;
	}
	return *this;
}

//============================================================================
void VxInetAddrAndPort::setIpAndPort( struct sockaddr_storage& oAddr )
{
	m_u16Port = setIp( oAddr );
}
